package controller;

import model.File;
import model.Network;
import model.connectible.ISP;
import model.connectible.Peer;
import model.connections.Connection;
import model.connections.Route;

import java.util.ArrayList;
import java.util.List;

import controller.utility.AverageCounter;

public class FileTransferServiceForP4P implements FileTransferService {
	private AverageCounter stabilityCounter;
	private AverageCounter distanceCounter;
	private AverageCounter transferCounter;
	
	public FileTransferServiceForP4P() {
		stabilityCounter = new AverageCounter();
		distanceCounter = new AverageCounter();
		transferCounter = new AverageCounter();
	}

	/*
	 * To do: implement maximum-flow-problem-solving algorithm.
	 * Update: prototype algorithm encoded.
	 */
	public int getMaximumRouteFlow(Route route) {
		int maximumRouteFlow = Integer.MAX_VALUE;
		for(int i = 0; i < route.getConnections().size(); i++) {
			maximumRouteFlow = Math.min(maximumRouteFlow, route.getConnections().get(i).getBandwidth());
//			System.out.println("Connection bandwidth: "+route.getConnections().get(i).getBandwidth());
		}
//		System.out.println("max data flow" +maximumRouteFlow);
//		System.out.println(route.getConnections().size());
		return maximumRouteFlow;
	}
	
	public double getResultantStability(Route route) {
		double stability = 1;
		for(int i = 0; i < route.getConnections().size(); i++) {
			stability *= route.getConnections().get(i).getStability();
		}
		return stability;
	}
	
	/* 
	 * To do: implement best-route-from-peer-to-file-finding algorithm.
	 */
	
//	public Route getBestRoute(Peer peer) {
//		Route route = new Route(peer.getIspConnection(), peer.getWantedFile());
//		route.setDataFlow(getMaximumRouteFlow(route));
//		return route;
//	}
	
	public void evaluateAllRoutes(List<Route> routes) {
		for (Route route : routes) {
			route.setDataFlow(getMaximumRouteFlow(route));
			route.setStability(getResultantStability(route));
			route.setEvaluation(route.getDataFlow()*route.getStability()/route.getConnections().size());
		}
	}
	
	public Route getBestRoute(List<Route> routes) {
        if (routes.size() > 0) {
            Route bestRoute = routes.get(0);
            for (Route route : routes) {
//			if(route.getDataFlow() > bestRoute.getDataFlow()) {
                if (route.getEvaluation() > bestRoute.getEvaluation()) {
//            	if (route.getConnections().size() < bestRoute.getConnections().size()) {
                    bestRoute = route;
                }
            }
            return bestRoute;
        }
        if (routes.size() == 0) {
//        	System.out.println("no routes avaliable");
        }
        return null;
	}
	
	public List<Peer> getAllPeers(Peer requestingPeer, File wantedFile, Network network) {
//		for(int i = 0; i < )
		return null;
	}
	
	public void sendRequest(Peer peer) {
		//peer.setRoute(getBestRoute(peer));
		Route route = new Route(peer.getIspConnection(), peer.getWantedFile());
		ArrayList<Route> routes = new ArrayList<Route>();
		routes.addAll(sendRequest((ISP)peer.getIspConnection().getOther(peer), route));
		if(!routes.isEmpty()) {
//			System.out.println("found");
		}
		evaluateAllRoutes(routes);
		peer.setRoute(getBestRoute(routes));
		if (peer.getRoute() == null) {
			System.out.println(peer.getId()+"route missing");
		}
		if (peer.getRoute() != null) {
			stabilityCounter.add(peer.getRoute().getStability());
			distanceCounter.add(peer.getRoute().getConnections().size());
			transferCounter.add(peer.getRoute().getDataFlow());
		}
		
	}
	
	public List<Route> sendRequest(ISP isp, Route route) {
		for (Connection c : route.getConnections()) {
//			System.out.print("["+c.getNodeA().getId()+" -> "+c.getNodeB().getId()+"] -> ");	
		}
//		System.out.println();
		ArrayList<Route> routes = new ArrayList<Route>();
		if(!isp.isChecked()) {
			isp.setChecked(true);
			ArrayList<Peer> peersWithFile = getPeerWithFile(isp, route.getWantedFile());
			if(!peersWithFile.isEmpty()) {
				for(int i = 0; i < peersWithFile.size(); i++) {
					Route r = route.clone();
					r.addConnection(peersWithFile.get(i).getIspConnection());
					routes.add(r);
				}
			} else {
				for(int i = 0; i < isp.getIspConnections().size(); i++) {
					Route r = route.clone();
					Connection c = isp.getIspConnections().get(i);
					r.addConnection(c);
					routes.addAll(sendRequest((ISP) c.getOther(isp), r));
				}
			}
		}
		return routes;
	}
	
	public ArrayList<Peer> getPeerWithFile(ISP isp, File wantedFile) {
		
		ArrayList<Peer> peersWithFile = new ArrayList<Peer>();
		for (int i = 0; i < isp.getLocalConnections().size(); i++) {
			
			Peer peer = (Peer)isp.getLocalConnections().get(i).getOther(isp);
//			System.out.print("["+peer.getId());
			if(peer.isOn() && peer.isSeeedingFile(wantedFile)) {
				peersWithFile.add(peer);
			} else {
//				System.out.print(" no-"+wantedFile.getId()+" "+((peer.isOn()) ? "on" : "off"));
			}
//			System.out.print("]");
		}
		return peersWithFile;
	}

	@Override
	public double getAverageStability() {
		return stabilityCounter.getAverage();
	}

	@Override
	public double getAverageTransfer() {
		return transferCounter.getAverage();
	}

	@Override
	public double getAverageJumpCount() {
		System.out.println("jump count count: "+stabilityCounter.getCounter());
		return distanceCounter.getAverage();
	}

	@Override
	public double getLocalMovementPercent() {
		return 2/distanceCounter.getAverage();
	}
	
}
